using System;
using System.Collections.Generic;
using System.Threading;
using Microsoft.TeamFoundation.Client;
using Microsoft.TeamFoundation.Server;
using Microsoft.TeamFoundation.VersionControl.Client;
using Microsoft.TeamFoundation.WorkItemTracking.Client;
using EnvDTE80;
using Microsoft.VisualStudio.TeamFoundation.VersionControl;
using EnvDTE;
using Microsoft.VisualStudio.TeamFoundation;

namespace TeamReview.Services
{
    public static class TFSService
    {
        private static TeamFoundationServer tfs;
        private static readonly List<string> users = new List<string>();
        private static readonly object userLock = new Object();
        private static TeamProject teamProject;
        private static WorkItemStore workItemStore;
        private static FieldDefinitionCollection fields;
        private static AllowedValuesCollection priorityAllowedValues;
        private static DTE2 shellDTE;
        private static SolutionEvents solutionEvents;
        public static event TeamProjectChangeEventHandler TeamProjectChanged;
        private static VersionControlServer versionControlServer;
        private static TeamFoundationServerExt tfsExt;
        private static VersionControlExt versionControl;

        private static void OnTeamProjectChanged()
        {

            if (teamProject == null)
                Instrumentation.Trace("TeamReview is not available because the current solution is not part of a Team Project.");
            else
                Instrumentation.Trace(String.Format(@"Team Project changed to {0}\{1}",
                                          teamProject.TeamFoundationServer.Name, teamProject.Name));

            if (TeamProjectChanged == null)
                return;

            TeamProjectChanged();
        }


        public static VersionControlServer VersionControlServer
        {
            get { return VersionControlServer; }
        }

        public static TeamProject TeamProject
        {
            get { return teamProject; }
        }

        public static VersionControlExt VersionControlExt
        {
            get { return versionControl; }
        }

        public static TeamFoundationServerExt TeamFoundationServerExt
        {
            get { return tfsExt; }
        }

        public static TeamFoundationServer TeamFoundationServer
        {
            get { return tfs; }
        }

        public static WorkItemStore WorkItemStore
        {
            get { return workItemStore; }
        }

        public static StoredQueryCollection StoredQueries
        {
            get
            {
                return workItemStore.Projects[teamProject.Name].StoredQueries;
            }
        }

        public static string CurrentUserDisplayName
        {
            get
            {
                if (workItemStore.UserDisplayMode == UserDisplayMode.AccountName)
                    return workItemStore.TeamFoundationServer.AuthenticatedUserName;

                return workItemStore.TeamFoundationServer.AuthenticatedUserDisplayName;
            }
        }

        public static bool IsTFSProject
        {
            get
            {
                return !(teamProject == null);
            }
        }

        public static void Configure(DTE2 dte)
        {
            shellDTE = dte;
            solutionEvents = shellDTE.Events.SolutionEvents;
            solutionEvents.Opened += new _dispSolutionEvents_OpenedEventHandler(SetTeamProject);
            SetTeamProject();
        }

        public static void Shutdown()
        {
            try
            {
                solutionEvents.Opened -= new _dispSolutionEvents_OpenedEventHandler(SetTeamProject);
            }
            catch { }
        }


        public static bool HasField(string fieldName)
        {
            return fields.Contains(fieldName);
        }


        static void SolutionEvents_Opened()
        {
            SetTeamProject();
        }



        public static AllowedValuesCollection PriorityAllowedValues
        {
            get { return priorityAllowedValues; }
        }

        public static void SetTeamProject()
        {

            TeamProject newProject;

            if (shellDTE.Solution == null)
                newProject = null;
            else
            {
                string solution = "not open";
                if (!String.IsNullOrEmpty(shellDTE.Solution.FullName))
                    solution = shellDTE.Solution.FullName;

                Instrumentation.Trace(String.Format(@"Visual Studio Solution is {0}", solution));


                versionControl = shellDTE.GetObject("Microsoft.VisualStudio.TeamFoundation.VersionControl.VersionControlExt") as VersionControlExt;
                if (versionControl == null || versionControl.SolutionWorkspace == null)
                    newProject = null;
                else
                    newProject = versionControl.SolutionWorkspace.GetTeamProjectForLocalPath(shellDTE.Solution.FullName);
            }

            if (teamProject != newProject)
            {
                teamProject = newProject;

                if (teamProject == null)
                {
                    OnTeamProjectChanged();
                    return;
                }


                tfs = teamProject.TeamFoundationServer;
                tfsExt = (TeamFoundationServerExt)shellDTE.GetObject("Microsoft.VisualStudio.TeamFoundation.TeamFoundationServerExt");
                versionControlServer = (VersionControlServer)tfs.GetService(typeof(VersionControlServer));

                workItemStore = (WorkItemStore)tfs.GetService(typeof(WorkItemStore));
                if (workItemStore == null)
                    throw new NullReferenceException("Did not retrieve WorkItemStore from Team Foundation Server");
                fields = workItemStore.Projects[teamProject.Name].WorkItemTypes[Constants.CODE_REVIEW_ITEM_TYPE].FieldDefinitions;

                if (fields.Contains(Constants.CODE_PRIORITY_FIELD))
                {
                    priorityAllowedValues = fields[Constants.CODE_PRIORITY_FIELD].AllowedValues;

                }
                else
                {
                    priorityAllowedValues = null;
                }

                System.Threading.Thread thread = new System.Threading.Thread(new ThreadStart(SetUsers));
                thread.Start();
                OnTeamProjectChanged();
            }
        }

        public static TeamProject GetTeamProject()
        {
            return teamProject;
        }

        private static void SetUsers()
        {
            lock (userLock)
            {
                if (tfs == null)
                    return;

                users.Clear();

                IGroupSecurityService gss = (IGroupSecurityService)tfs.GetService(typeof(IGroupSecurityService));

                Identity[] projectGroups = gss.ListApplicationGroups(teamProject.ArtifactUri.ToString());
                Identity serviceGroup = gss.ReadIdentity(SearchFactor.ServiceApplicationGroup, null, QueryMembership.None);
                Identity[] serviceGroups = new Identity[1] { serviceGroup };

                AddCodeChangers(gss, projectGroups);
                AddCodeChangers(gss, serviceGroups);

                users.Sort();
            }
        }

        private static void AddCodeChangers(IGroupSecurityService gss, Identity[] projectGroups)
        {
            foreach (Identity projectGroup in projectGroups)
            {
                if (!(projectGroup.DisplayName.Equals("Contributors") ||
                      projectGroup.DisplayName.Equals("Project Administrators") ||
                      projectGroup.DisplayName.Equals("Service Accounts")))
                    continue;

                Identity directMembers = gss.ReadIdentity(SearchFactor.Sid, projectGroup.Sid, QueryMembership.Expanded);
                foreach (string memberSid in directMembers.Members)
                {
                    Identity member = gss.ReadIdentity(SearchFactor.Sid, memberSid, QueryMembership.None);

                    if (!member.SecurityGroup && member.Type != IdentityType.WindowsGroup
                        && member.Type != IdentityType.ApplicationGroup)
                    {
                        if (!users.Contains(member.DisplayName))
                            users.Add(member.DisplayName);
                    }
                }
            }
        }

        public static List<string> GetTeamUsers()
        {
            lock (userLock)
            {
                return users;
            }
        }

        public static WorkItemTypeCollection GetAvailableTeamWorkItemTypes
        {
            get { return workItemStore.Projects[teamProject.Name].WorkItemTypes; }
        }
    }
}
